import sys
import cv2 as cv
from PySide6.QtWidgets import (QApplication, QMainWindow, QLabel, QPushButton,
                               QVBoxLayout, QHBoxLayout, QWidget, QSlider, QMessageBox)
from PySide6.QtCore import QThread, Signal, Qt, QTimer
from PySide6.QtGui import QImage, QPixmap
from ultralytics import YOLO
from monitoring_system import ImageProcessingUI



# -------------------------- 解决QApplication单例冲突 --------------------------
def get_or_create_qapp():
    """获取已存在的QApplication实例，不存在则创建（适配交互式环境）"""
    app = QApplication.instance()
    if app is None:
        app = QApplication(sys.argv)
    return app


# -------------------------- 第二个界面：检测到目标后跳转的界面 --------------------------
class SecondWindow(QMainWindow):
    """检测到目标后跳转的第二个界面"""

    def __init__(self):
        super().__init__()
        self.setWindowTitle("检测结果界面")
        self.setGeometry(200, 200, 600, 400)

        # 设置中心部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)

        # 添加界面内容
        title_label = QLabel("成功检测到目标！")
        title_label.setAlignment(Qt.AlignCenter)
        title_label.setStyleSheet("font-size: 24px; font-weight: bold; color: #2ecc71;")

        info_label = QLabel("系统已成功识别到 'li' 类别目标。")
        info_label.setAlignment(Qt.AlignCenter)
        info_label.setStyleSheet("font-size: 16px; margin: 20px 0;")

        back_btn = QPushButton("返回检测界面")
        back_btn.setStyleSheet("background-color: #3498db; color: white; padding: 10px; font-size: 14px;")
        back_btn.clicked.connect(self.go_back)

        layout.addStretch()
        layout.addWidget(title_label)
        layout.addWidget(info_label)
        layout.addWidget(back_btn, alignment=Qt.AlignCenter)
        layout.addStretch()

    def go_back(self):
        """返回检测界面"""
        self.main_window = YOLODetectorWindow()
        self.main_window.show()
        self.close()


# -------------------------- 检测线程类 --------------------------
class DetectionThread(QThread):
    frame_processed = Signal(QImage)
    detection_info = Signal(str)
    target_detected = Signal()  # 检测到目标的信号

    def __init__(self, model, parent=None):
        super().__init__(parent)
        self.model = model
        self.running = False
        self.conf_threshold = 0.8
        self.clses = ['li']  # 要检测的目标类别
        self.target_found = False  # 标记是否已检测到目标

    def set_conf_threshold(self, threshold):
        self.conf_threshold = threshold / 100.0

    def run(self):
        self.running = True
        self.target_found = False  # 重置目标检测标记
        cap = cv.VideoCapture(0)

        while self.running and cap.isOpened() and not self.target_found:
            ret, frame = cap.read()
            if not ret:
                self.detection_info.emit("摄像头读取失败")
                break

            # 目标检测
            results = self.model(frame, imgsz=320, device=0, half=True, verbose=False)
            detected_count = 0
            target_detected_flag = False  # 标记当前帧是否检测到目标

            for result in results:
                boxes = result.boxes.cpu()  # 从CUDA转移到CPU
                for box in boxes:
                    x1, y1, x2, y2 = box.xyxy.numpy()[0].tolist()
                    x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
                    conf = float(box.conf)
                    cls = int(box.cls)

                    if conf > self.conf_threshold:
                        detected_count += 1
                        # 绘制边界框和标签
                        cv.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 255), 2)
                        label = f'{self.clses[cls]}: {conf:.2f}'
                        cv.putText(frame, label, (x1, y1 + 20),
                                   cv.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

                        # 检查是否检测到目标类别
                        if self.clses[cls] == 'li':
                            target_detected_flag = True

            # 发送检测信息
            self.detection_info.emit(f"检测到目标数量: {detected_count} | 置信度阈值: {self.conf_threshold:.1%}")

            # 转换为QImage并发送
            rgb_frame = cv.cvtColor(frame, cv.COLOR_BGR2RGB)
            h, w, ch = rgb_frame.shape
            q_image = QImage(rgb_frame.data, w, h, ch * w, QImage.Format_RGB888)
            self.frame_processed.emit(q_image)

            # 如果检测到目标，设置标记并发送信号
            if target_detected_flag:
                self.target_found = True
                self.target_detected.emit()  # 发送目标检测到的信号

        cap.release()

    def stop(self):
        self.running = False
        self.wait()


# -------------------------- 主检测窗口类 --------------------------
class YOLODetectorWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.second_window = None  # 第二个窗口实例
        self.init_window()
        self.model = self.load_yolo_model()
        self.init_ui()
        self.init_thread()
        self.countdown_timer = QTimer(self)  # 倒计时计时器
        self.countdown_timer.timeout.connect(self.update_countdown)
        self.remaining_time = 0  # 剩余倒计时时间

    def init_window(self):
        self.setWindowTitle("YOLOv8 实时目标检测器")
        self.setGeometry(100, 100, 850, 650)
        self.setMinimumSize(600, 400)

    def load_yolo_model(self):
        try:
            model = YOLO(r'.\static\best.pt')
            model.eval()
            return model
        except FileNotFoundError:
            print("错误：模型文件未找到！请检查模型路径：")
            QMessageBox.critical(self, "模型加载失败", "未找到模型文件，请检查路径是否正确。")
            return None
        except Exception as e:
            print(f"模型加载失败：{str(e)}")
            QMessageBox.critical(self, "模型加载失败", f"加载模型时发生错误：{str(e)}")
            return None

    def init_ui(self):
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        main_layout.setSpacing(10)
        main_layout.setContentsMargins(15, 15, 15, 15)

        # 视频显示区
        self.video_label = QLabel("等待检测开始...")
        self.video_label.setAlignment(Qt.AlignCenter)
        self.video_label.setStyleSheet("border: 1px solid #cccccc; border-radius: 5px;")
        main_layout.addWidget(self.video_label, stretch=1)

        # 信息区
        self.info_label = QLabel("状态：未开始检测 | 请点击「开始检测」按钮")
        self.info_label.setStyleSheet("color: #666666; font-size: 14px;")
        main_layout.addWidget(self.info_label)

        # 控制区
        control_layout = QHBoxLayout()
        self.conf_slider = QSlider(Qt.Horizontal)
        self.conf_slider.setRange(50, 99)
        self.conf_slider.setValue(80)
        self.conf_slider.setTickInterval(5)
        self.conf_slider.setTickPosition(QSlider.TicksBelow)
        self.conf_slider.valueChanged.connect(self.update_conf_threshold)

        control_layout.addWidget(QLabel("置信度阈值："))
        control_layout.addWidget(self.conf_slider)
        self.conf_value_label = QLabel("80%")
        control_layout.addWidget(self.conf_value_label)

        # 按钮
        self.start_btn = QPushButton("开始检测")
        self.start_btn.clicked.connect(self.start_detection)
        self.start_btn.setStyleSheet(
            "background-color: #2ecc71; color: white; border: none; padding: 8px 15px; border-radius: 3px;")

        self.stop_btn = QPushButton("停止检测")
        self.stop_btn.clicked.connect(self.stop_detection)
        self.stop_btn.setEnabled(False)
        self.stop_btn.setStyleSheet(
            "background-color: #e74c3c; color: white; border: none; padding: 8px 15px; border-radius: 3px;")

        control_layout.addStretch(1)
        control_layout.addWidget(self.start_btn)
        control_layout.addWidget(self.stop_btn)
        main_layout.addLayout(control_layout)

    def init_thread(self):
        self.detection_thread = DetectionThread(self.model)
        self.detection_thread.frame_processed.connect(self.update_video_frame)
        self.detection_thread.detection_info.connect(self.update_info_label)
        self.detection_thread.target_detected.connect(self.on_target_detected)  # 绑定目标检测信号

    def start_detection(self):
        if self.model is None:
            self.update_info_label("错误：模型未加载，无法开始检测！")
            return

        if not self.detection_thread.isRunning():
            self.detection_thread.start()
            self.start_btn.setEnabled(False)
            self.stop_btn.setEnabled(True)
            self.update_info_label("状态：正在检测...")

    def stop_detection(self):
        if self.detection_thread.isRunning():
            self.detection_thread.stop()
            self.start_btn.setEnabled(True)
            self.stop_btn.setEnabled(False)
            self.video_label.setText("检测已停止 | 点击「开始检测」重新启动")
            self.update_info_label("状态：检测已停止")

        # 停止倒计时
        if self.countdown_timer.isActive():
            self.countdown_timer.stop()

    def update_video_frame(self, q_image):
        scaled_image = q_image.scaled(self.video_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
        self.video_label.setPixmap(QPixmap.fromImage(scaled_image))

    def update_info_label(self, info):
        self.info_label.setText(f"状态：{info}")

    def update_conf_threshold(self):
        current_conf = self.conf_slider.value()
        self.conf_value_label.setText(f"{current_conf}%")
        self.detection_thread.set_conf_threshold(current_conf)

    def on_target_detected(self):
        """当检测到目标时调用"""
        # 停止检测
        self.detection_thread.stop()
        self.start_btn.setEnabled(True)
        self.stop_btn.setEnabled(False)

        # 显示检测到目标的信息
        self.update_info_label("状态：已检测到 'li' 目标！将在3秒后跳转...")

        # 开始5秒倒计时
        self.remaining_time = 3
        self.countdown_timer.start(1000)  # 每秒触发一次

    def update_countdown(self):
        """更新倒计时"""
        self.remaining_time -= 1
        self.update_info_label(f"状态：已检测到 'li' 目标！将在 {self.remaining_time} 秒后跳转...")

        if self.remaining_time <= 0:
            self.countdown_timer.stop()
            self.navigate_to_second_window()

    def navigate_to_second_window(self):
        """跳转到第二个界面"""
        self.second_window = ImageProcessingUI()
        self.second_window.main_window = self  # 保存主窗口引用，以便返回
        self.second_window.show()
        self.hide()  # 隐藏当前窗口

    def closeEvent(self, event):
        """窗口关闭时的清理操作"""
        if self.detection_thread.isRunning():
            self.detection_thread.stop()
        if self.countdown_timer.isActive():
            self.countdown_timer.stop()
        # 如果第二个窗口存在，也关闭它
        if self.second_window and not self.second_window.isHidden():
            self.second_window.close()
        event.accept()


# -------------------------- 主程序入口 --------------------------
if __name__ == "__main__":
    app = get_or_create_qapp()
    window = YOLODetectorWindow()
    window.show()

    # 处理交互式环境和普通脚本环境的差异
    if hasattr(sys, 'ps1'):
        app.exec()
    else:
        sys.exit(app.exec())
